<?php

/*
  ZoneData help to make a check form submit quickly
  Result is a instance of  ZoneZDataResult:
 */

class ZDataResult{

	/*
		data of object get from acceptable submit
	*/
	public $data = array();	
	
	public $error = array();
		
	public function isError(){
		return count($this->error) > 0;
	}
	
	public function isValid(){
		return !$this->isError();
	}
	
	public function setData( $data, $value ){
		if( is_array( $data ) ){
			foreach( $data as $k => $v )
				$this->data[$k] = $v;
			return $this;	
		}
		$this->data[$data] = $value;
		return $this;	
	}

	public function setError( $field, $message ){
		$this->error[$field] = $message;
		return $this;
	}
	
	public function getData(){
		return $this->data;
	}

	public function getError(){
		$error = $this->error;
		return func_num_args() == 0 
			? $error : $error[func_get_arg(0)];
	}
} 
 
class ZData {
    /* store all normal fields */

    //protected 
	public $_fields = array();

    /* cache file uploaded */
    protected $_Cache = array();

    /* namespace of $_POST & $_FILE */
    protected $_IndexRequest = NULL;

    /* index of item for mul-fields */
    protected $_IndexItem = NULL;

    /*
      Register a field to data
    */
	public function addField($field, $config=array()) {
		return $this->addFields($field, $config);
	}
	 
    public function addFields($field, $config=array()) {
        if (is_array($field)) {
            foreach ($field as $a => $v) {
                $this->addFields($a, $v);
            }
            return $this;
        }

        if (!isset($config['name'])) {
            $config['name'] = $field;
        }

		//set and fix from old version
		$config['label'] = isset($config['label']) 
			? $config['label'] : ( isset($config['form_label']) ? $config['form_label'] : $field );		
		
        $config['type'] = strtoupper($config['type']);

        $this->_fields[$field] = $config;
    }

    /*
      Change a field settings
     */

    public function changeField($field, $key, $value) {
        if (isset($this->_fields[$field])) {
            $this->_fields[$field][$key] = $value;
        }
    }

    /*
      Remove a field settings
     */

    public function removeField($field) {
        if (isset($this->_fields[$field])) {
            unset($this->_fields[$field]);
        }
    }

    /*
      If a form which contain field upload
      return a error then we need free cache
     */

    public function removeCache() {
        $files = $this->_Cache;
        while (count($files) > 0) {
            @unlink(array_shift($files));
        }
    }

    public function setIndexRequest($idx) {
        $this->_IndexRequest = $idx;
    }

    public function setIndexItem($idx) {
        $this->_IndexItem = $idx;
    }

	/*
		The short way to getResult with return the first error
	*/
	public function getData(){
		
		$result = $this->getResult();
		if( $result->isError()){
			$error = $result->getError();
			$s='';
			foreach( $error as $f => $m ){
				$s .="<div>+) $m</div>";
			}
			return $s;
		}
		
		return $result->getData();
	}

    /*
      Validator data submit and return status
     */
	 
    public function getResult() {
        $data = array();
        $result = new ZDataResult();

        if (empty($this->_IndexRequest)) {
            $REQUEST = $_REQUEST;
            $FILES = $_FILES;
        } else {
            $REQUEST = (array) $_REQUEST[$this->indexRequest];
            $FILES = (array) $_FILES[$this->indexRequest];
        }

        foreach ($this->_fields as $field => $config) {

            $k = $config['name'];
            $upper_label = utf8_ucfirst($config['label']);
            $lower_label = utf8_lcfirst($config['label']);

            $type = strtoupper($config['type']);

            if (in_array($type, array('FILE', 'IMAGE', 'BINARY'))) {
                continue;
            }

            if ($config['type'] == 'NONE') {
                $result->setData($field, $config['default_value']);
                continue;
            }

            /*
              ENUM & LIST is a special field
             */
            if ($type == 'ENUM') {

                if ($this->_IndexItem == NULL) {
                    $v = $REQUEST[$k];
                } else {
                    $v = $REQUEST[$k][$this->_IndexItem];
                }

                $enum_value = (array) $config['value'];

                if (!in_array($v, $enum_value)) {
                    if (array_key_exists('default_value', $config)) {
                        $result->setData($field, $config['default_value']);
                    }
                }else{
					$result->setData( $field, $v);
				}	
                continue;
            }

           if ($type == 'LIST') {

                if ($this->_IndexItem == NULL) {
                    $v = $REQUEST[$k];
                } else {
                    $v = $REQUEST[$k][$this->_IndexItem];
                }

                $list_value = (array) $config['value'];
				$r = array();
				
				foreach( (array)$v as $i ){
					if ( in_array($i, $list_value)) {
						$r[] = $i;
					}	
				}	
				
				if( count($r) > 0 ){
					$result->setData($field, implode( ( isset($config['separator']) ? $config['separator'] : ','),$r));
				}else{
					if (array_key_exists('default_value', $config)) {
						$result->setData($field, $config['default_value']);
					}				
				}
                continue;
            }

            if ($this->_IndexItem == NULL) {
                if (empty($REQUEST[$k])) {
                    if ($config['no_empty']) {
                        $result->setError(
                                $k, isset($config['error_empty_message']) ? $config['error_empty_message'] : "$upper_label bắt buộc phải nhập"
                        );
                    } else {
                        if (array_key_exists('default_value', $config)) {
                            $result->setData($field, $config['default_value']);
                        } else {
                            $result->setData($field, isset($REQUEST[$k]) ? $REQUEST[$k] : NULL);
                        }
                        continue;
                    }
                }
            } else {
                if (empty($REQUEST[$k][$this->_IndexItem])) {
                    if ($config['no_empty']) {
                        $result->setError(
                                $k, isset($config['error_empty_message']) ? $config['error_empty_message'] : "$upper_label bắt buộc phải nhập"
                        );
                    } else {
                        if (array_key_exists('default_value', $config)) {
                            $result->setData($field, $config['default_value']);
                        } else {
                            $result->setData($field, $REQUEST[$k][$this->_IndexItem] ? $REQUEST[$k][$this->_IndexItem] : NULL);
                        }
                        continue;
                    }
                } else {
                    if (!$is_file && !$is_files) {
                        $result->setData($field, isset($REQUEST[$k][$this->_IndexItem]) ? $REQUEST[$k][$this->_IndexItem] : NULL);
                    }
                    continue;
                }
            }

            //because when use update and insert
            if ($this->_IndexItem === NULL) {
                $value = $REQUEST[$k];
            } else {
                $value = $REQUEST[$k][$this->_IndexItem];
            }

            if (isset($config['fix_value']) && function_exists($config['fix_value'])) {
                $value = $config['fix_value']($value);
            }

            if (!is_array($value)) {
                $value = stripslashes($value);
            } else {
                foreach ($value as $j => $v) {
                    $value[$j] = stripslashes($v);
                }
            }

            /* Some config for all field type */
            if (isset($config['min_length'])) {
                if (strlen($value) < (int) $config['min_length']) {
                    $result->setError(
                            $k
                            , utf8_ucfirst($config['label']) . " quá ngắn ( tối thiểu {$config['min_length']} kí tự )"
                    );
                }
            }

            if (isset($config['max_length'])) {
                if (strlen($value) > (int) $config['max_length']) {
                    $result->setError(
                            $k
                            , utf8_ucfirst($config['label']) . " quá dài  ( tối đa {$config['max_length']} kí tự )."
                    );
                    continue;
                }
            }

            //check preg_match
            if (isset($config['mathes'])) {
                foreach ($config['mathes'] as $match => $message) {
                    if (!preg_match($match, $value)) {
                        $result->setError(
                                $k, preg_replace(array("{Label}", "{label}"), array(utf8_ucfirst($config['label']), utf8_lcfirst($config['label'])), $message)
                        );
                        break;
                    }
                }
            }


            switch ($type) {
                /* Simple type a non-html string or html string */
                case 'TEXT':
                case 'CHAR':
                case 'HTML':
                    $result->setData(
                            $field, $this->filter($type != 'HTML' ? escape_html($value) : $value, $config)
                    );
                    break;

                case 'EMAIL':
                    if (isEmail($value)) {
                        $result->setData(
                                $field, $this->filter($value, $config)
                        );
                    } else {
                        $result->setError(
                                $k, isset($config['error_format_message']) ? $config['error_format_message'] : "$upper_label không hợp lệ"
                        );
                    }
                    break;

                case 'DATE':
                    if ($value === '' || date('Y-m-d', strtotime($value)) == $value) {
                        $result->setData(
                                $field, $this->filter($value, $config)
                        );
                    } else {
                        $result->setError(
                                $k, $config['error_format_message'] ? $config['error_format_message'] : "$upper_label không hợp lệ"
                        );
                    }
                    break;

                case 'DATETIME':
                    if ($value === '' || date('Y-m-d H:i:s', strtotime($value)) == $value) {
                        $result->setData(
                                $field, $this->filter($value, $config)
                        );
                    } else {
                        $result->setError(
                                $k, $config['error_max_message'] ? $config['error_max_message'] : "$upper_label không hợp lệ"
                        );
                    }
                    break;

                case 'INT':
                    if (is_int(intval($value))) {
                        //check value in range set
                        $num = intval($value);
                        if (isset($config['max'])) {
                            if ($num > $config['max']) {
                                $result->setError(
                                        $k, $config['error_max_message'] ? $config['error_max_message'] : "$upper_label không thể lớn hơn {$config['max']}"
                                );
                                break;
                            }
                        }

                        if (isset($config['min'])) {
                            if ($num < $config['min']) {
                                $result->setError(
                                        $k, $config['error_min_message'] ? $config['error_min_message'] : "$upper_label không thể nhỏ hơn {$config['min']}"
                                );
                                break;
                            }
                        }

                        $result->setData(
                                $field, $this->filter($value, $config)
                        );
                    } else {
                        $result->setError(
                                $k, $config['error_format_message'] ? $config['error_format_message'] : "$upper_label phải là một số nguyên"
                        );
                    }
                    break;

                case 'FLOAT':
                    if (is_float(floatval($value))) {
                        //check value in range set
                        $num = intval($value);
                        if (isset($config['max'])) {
                            if ($num > $config['max']) {
                                $result->setError(
                                        $k, $config['error_max_message'] ? $config['error_max_message'] : "$upper_label  không được lớn hơn {$config['max']}"
                                );
                                break;
                            }
                        }

                        if (isset($config['min'])) {
                            if ($num < (int) $config['min']) {
                                $result->setError(
                                        $k, $config['error_min_message'] ? $config['error_min_message'] : "$upper_label không thể nhỏ hơn."
                                );
                                break;
                            }
                        }
                        $result->setData(
                                $field, $this->filter($value, $config)
                        );
                    } else {
                        $result->setError(
                                $k, $config['error_format_message'] ? $config['error_format_message'] : "$upper_label phải là một số thực"
                        );
                    }
                    break;

                case 'PASSWORD':
                    if ($this->_IndexItem === NULL) {
                        $value = $REQUEST[$k];
                        $re_pass = $REQUEST['re_' . $k];
                        $old_pass = $REQUEST['old_' . $k];
                    } else {
                        $value = $REQUEST[$k][$this->_IndexItem];
                        $re_pass = $REQUEST['re_' . $k][$this->_IndexItem];
                        $old_pass = $REQUEST['old_' . $k][$this->_IndexItem];
                    }

                    if ($value != $re_pass) {

                        $result->setError(
                                $k, $config['error_match_message'] ? $config['error_match_message'] : "$upper_label không đồng bộ"
                        );
                        break;
                    } else if (isset($config['old_value']) && md5($old_pass) != $config['old_value']) {

                        $result->setError(
                                $k, $config['error_current_pass'] ? $config['error_current_pass'] : "$upper_label cũ không đúng"
                        );
                        break;
                    }

                    $result->setData($field, $this->filter(md5($value), $config));

                    break;
                default:
            }
        }
        return $this->validFile( $result );
    }

    protected function validFile($result) {

        if (empty($this->_IndexRequest)) {
            $FILES = $_FILES;
        } else {
            $FILES = (array) $_FILES[$this->_IndexRequest];
        }

        foreach ($this->_fields as $field => $config) {
            $k = $config['name'];
            $type = strtoupper($config['type']);

            if (!in_array($type, array('FILE','IMAGE','BINARY')))
                continue;

            switch ($type) {

                case 'IMAGE':
                    $config['file_type'] = array('gif', 'png', 'jpg', 'jpeg');

                case 'FILE':
                    if ($this->_IndexItem === NULL) {
                        $check = $this->checkFile($FILES[$k], $config);
                    } else {
                        $check = $this->checkFile($FILES[$k][$this->_IndexItem], $config);
                    }

                    if (!is_array($check)) {
                        $this->removeCache();
                        return $result->setError($k, $check);
                    } else {
                        $this->_Cache[] = str_replace('//', '/', $config['path'] . '/' . $check[0]);
                        $result->setData(
							$field,
							$config['return_name'] ? $check[0] : $check
						);
                    }
                    break;

                case 'BINARY':
                    $check = $this->checkBinary($FILES[$k], $config);
                    if (!is_array($check)) {
                        $this->removeCache();
                        return $result->setError($k, $check);
                    } else {
                        $result->setData(
							$field,
							$config['return_name'] ? $check[0] : $check
						);
                    }
                    break;
                default:
            }
        }
		
		return $result;
    }

    //we try to upload whether is successful return error or $file_name 
    public function checkFile($tmp, $config = array()) {
        $config['file'] = $tmp;
        if (!isset($config['path']))
            $config['path'] = "files/";
        return upload($config);
    }

    public function checkBinary($tmp, $config = array()) {
        if ($config['file_type']) {
            if (!is_array($config['file_type']))
                $config['file_type'] = explode(',', $config['file_type']);
            $exts = array();
            $mime = include get_include_path() . '/Data/Mime.php';
            $mime = (array) $mime;
            foreach ($config['file_type'] as $k) {
                if (isset($mime[$k])) {
                    $exts = array_merge($exts, $mime[$k]);
                }
            }
            if (!in_array($tmp['type'], $exts)) {
                return "File " . utf8_lcfirst($config['label']) . " chỉ chấp nhận định dạng " . implode(',.', $config['file_type']);
            }
        }
        $max_size = (int) $config['max_size'];
        if ($max_size > 0 && $tmp['size'] > $max_size) {
            return "Dung lượng file " . utf8_lcfirst($config['label']) . " quá lớn";
        }

        $f = fopen($tmp['tmp_name'], "rb");
        $data = fread($f, $tmp['size']);
        fclose($f);

        return array(array($data, $tmp['type'], $tmp['size'], $tmp['name']));
    }

    public function filter($value, $config) {
        if (!isset($config['filter']) || !function_exists($config['filter']))
            return $value;
        $f = $config['filter'];
        return $f($value);
    }

}

?>